#include "stdio.h"
#include "wl_ext_fs.h"
#include "fcntl.h"
#include "sys/stat.h"

/*
    this file is for file system
    open, close, read, write, delete, seek, file size, file exists

    using relative path from ./, implemention should rebase path for real platform
*/

/**
 * open file by path
 * 
 * @param path file path
 * @param flags open flags, WL_RDONLY, WL_WRONLY, WL_RDWR, WL_CREAT, WL_APPEND
 * @return file handle success for non-null, fail for null
 */
wl_ext_fs_handle wl_ext_fs_open(const char* path, int flags) {
    const char* mode;
    if (flags & WL_EXT_RDWR) {
        mode = (flags & WL_EXT_CREAT) ? "w+b" : "r+b";
    } else if (flags & WL_EXT_WRONLY) {
        mode = (flags & WL_EXT_CREAT) ? "wb" : "rb";
    } else { // WL_EXT_RDONLY
        mode = "rb";
    }
    return (wl_ext_fs_handle)fopen(path, mode);
}

/**
 * close file by handle
 * 
 * @param handle file handle
 * @return 0 success, -1 fail
 */
int wl_ext_fs_close(wl_ext_fs_handle handle) {
    return fclose((FILE*)handle);

}

/**
 * read file by handle
 * 
 * @param handle file handle
 * @param buf read buffer
 * @param len bytes to read
 * @return bytes read, or < 0 for error
 */
int wl_ext_fs_read(wl_ext_fs_handle handle, void* buf, int len) {
    return fread(buf, 1, len, (FILE*)handle);
}

/**
 * write file by handle
 * 
 * @param handle file handle
 * @param buf write buffer
 * @param len bytes to write    
 * @return bytes written, or < 0 for error
 */
int wl_ext_fs_write(wl_ext_fs_handle handle, const void* buf, int len) {
    return fwrite(buf, 1, len, (FILE*)handle);
}

/**
 * delete file by path
 * 
 * @param path file path
 * @return 0 success, -1 fail
 */
int wl_ext_fs_delete(const char* path) {
    if (remove(path) == 0) {
        return 0; // success
    } else {
        return -1; // fail
    }
}

/**
 * seek file by handle
 * 
 * @param handle file handle
 * @param offset seek offset
 * @param whence EX_SEEK_SET, EX_SEEK_CUR, EX_SEEK_END
 * 
 * @return 0 success, -1 fail
 */
int wl_ext_fs_seek(wl_ext_fs_handle handle, int offset, int whence) {
    int origin;
    switch (whence) {
        case EX_SEEK_SET: origin = SEEK_SET; break;
        case EX_SEEK_CUR: origin = SEEK_CUR; break;
        case EX_SEEK_END: origin = SEEK_END; break;
        default: return -1; // invalid whence
    }
    return (fseek((FILE*)handle, offset, origin) == 0) ? 0 : -1;
}

/**
 * get file size by path
 * 
 * @param path file path
 * @return file size, or < 0 for error
 */
int wl_ext_fs_size_by_path(const char* path) {
    struct stat st;
    if (stat(path, &st) == 0) {
        return (int)st.st_size;
    } else {
        return -1; // error
    }
}

/**
 * get file size by handle
 * 
 * @param handle file handle
 * @return file size, or < 0 for error
 */
int wl_ext_fs_size_by_handle(wl_ext_fs_handle handle) {
    struct stat st;
    int fd = fileno((FILE*)handle);
    if (fd == -1 || fstat(fd, &st) != 0) {
        return -1; // error
    }
    return (int)st.st_size;
}

/**
 * check file exists by path
 * 
 * @param path file path
 * @return 0 not exists, 1 exists
 */
int wl_ext_fs_exists(const char* path) {
    struct stat st;
    if (stat(path, &st) == 0) {
        return 1; // exists
    } else {
        return 0; // not exists
    }
}

/**
 * rename file by path
 * 
 * @param src source path
 * @param dst destination path
 * @return 0 success, -1 fail
 */
int wl_ext_fs_rename(const char* src, const char* dst) {
    return rename(src, dst);
}
